-- UNUSED ---------------------- -- File: Mines -- Author: Shoker -- Модуль мин -- Swartz: Adapted a little further, however now you can blow up neutral stalkers with no penalty, (this needs to be fixed). -- Also, actor currently immune to them (needs to be since actor drops them at feet). -- Revamped by Alundaio to fix everything Swartz suggested -- Now turns NPCs who see actor place mines hostile -- Now has a primer before it will react to targets -- Uses persistent storage marshal library ---------------------- local BLOW_RADIUS = 2.5 local PRIMER_FACTOR = 5 -- Time to activate in human seconds local HOSTILE_TIME_FACTOR = 360 -- Amount of seconds before NPC forgets actor placed mine local neutral_placed_mines = {} local actor_placed_mines = {} local m_mine_table = { priming = {}, turn_hostile = {}, recently_placed = false } function add_actor_mine(m) actor_placed_mines[#actor_placed_mines+1] = m:id() m_mine_table.recently_placed = m:id() end function add_neutral_mine(m) neutral_placed_mines[#neutral_placed_mines+1] = m:id() end local function evaluate_actor_mines(npc,st) if (#actor_placed_mines == 0) then return end local tg = time_global() st.__timer_mine = not st.__timer_mine and tg+100 or st.__timer_mine if (tg < st.__timer_mine) then return end st.__index_mine = not st.__index_mine and 1 or st.__index_mine > #actor_placed_mines and 1 or st.__index_mine local mid = actor_placed_mines[st.__index_mine] local actor_mine = mid and level.object_by_id(mid) if not (actor_mine) then table.remove(actor_placed_mines,st.__index_mine) st.__index_mine = 1 return end -- Mine is m_mine_table.priming; give actor time to move away if (m_mine_table.priming[mid] and game.get_game_time():diffSec(m_mine_table.priming[mid]) < level.get_time_factor()*PRIMER_FACTOR) then st.__index_mine = st.__index_mine + 1 return end m_mine_table.priming[mid] = nil if (m_mine_table.recently_placed == mid) then m_mine_table.recently_placed = false end -- Blow up if NPC in radius if (npc:position():distance_to(actor_mine:position()) <= BLOW_RADIUS) then -- NPC saw actor place mine; turn hostile if (m_mine_table.turn_hostile[npc:id()]) then local h = hit() h.type = hit.fire_wound h.power = 0.0 h.impulse = 0.0 h.direction = VEC_Y h.draftsman = db.actor npc:hit(h) end actor_mine:explode(0) table.remove(actor_placed_mines,st.__index_mine) st.__index_mine = 1 return end st.__index_mine = st.__index_mine + 1 end local function evaluate_neutral_mines(npc,st) if (#neutral_placed_mines == 0) then return end st.__index_mine_n = not st.__index_mine_n and 1 or st.__index_mine_n > #neutral_placed_mines and 1 or st.__index_mine_n local mid = neutral_placed_mines[st.__index_mine_n] local neutral_mine = mid and level.object_by_id(mid) if not (neutral_mine) then table.remove(neutral_placed_mines,st.__index_mine_n) st.__index_mine_n = 1 return end -- Blow up if NPC in radius if (npc:position():distance_to(neutral_mine:position()) <= BLOW_RADIUS) then neutral_mine:explode(0) table.remove(neutral_placed_mines,st.__index_mine_n) st.__index_mine_n = 1 return end st.__index_mine_n = st.__index_mine_n + 1 end local function monster_on_update(npc,st) if not (npc:alive()) then return end evaluate_actor_mines(npc,st) evaluate_neutral_mines(npc,st) end local function npc_on_update(npc,st) if not (npc:alive()) then return end evaluate_actor_mines(npc,st) evaluate_neutral_mines(npc,st) local id = npc:id() local tg = time_global() if (m_mine_table.turn_hostile[id]) then if (game.get_game_time():diffSec(m_mine_table.turn_hostile[id]) > HOSTILE_TIME_FACTOR*level.get_time_factor()) then m_mine_table.turn_hostile[id] = nil end elseif (m_mine_table.recently_placed and npc:see(db.actor)) then if (npc:relation(db.actor) < game_object.enemy) then m_mine_table.turn_hostile[id] = game.get_game_time() end end end local function actor_on_update() if not (db.actor:alive()) then return end evaluate_actor_mines(db.actor,db.storage[0]) evaluate_neutral_mines(db.actor,db.storage[0]) local gtf = level.get_time_factor() if (m_mine_table.recently_placed and m_mine_table.priming[m_mine_table.recently_placed]) then local seconds = game.get_game_time():diffSec(m_mine_table.priming[m_mine_table.recently_placed]) if (seconds < gtf*PRIMER_FACTOR) then actor_menu.set_msg(1, math.floor((gtf*PRIMER_FACTOR-seconds)/gtf),1) else m_mine_table.priming[m_mine_table.recently_placed] = nil m_mine_table.recently_placed = false end end end local function actor_on_item_use(itm) local actor = db.actor if (itm:section() == "mine") then local se_obj = alife_create("mine_actor",actor:position(),actor:level_vertex_id(),actor:game_vertex_id()) if (se_obj) then m_mine_table.priming[se_obj.id] = game.get_game_time() actor_effects.play_item_fx("mine") end end if (itm:section() == "ied_rpg") then local se_obj = alife_create("rpg_blow",actor:position(),actor:level_vertex_id(),actor:game_vertex_id()) if (se_obj) then m_mine_table.priming[se_obj.id] = game.get_game_time() actor_effects.play_item_fx("ied_rpg") end end if (itm:section() == "ied") then local se_obj = alife_create("ied_blow",actor:position(),actor:level_vertex_id(),actor:game_vertex_id()) if (se_obj) then m_mine_table.priming[se_obj.id] = game.get_game_time() actor_effects.play_item_fx("ied") end end end local function save_state(data) --utils_data.debug_write("m_mines:save_state") data.m_mine_table = m_mine_table end local function load_state(data) if (data.m_mine_table) then m_mine_table = data.m_mine_table data.m_mine_table = nil end end function on_game_start() RegisterScriptCallback("npc_on_update",npc_on_update) RegisterScriptCallback("actor_on_update",actor_on_update) RegisterScriptCallback("monster_on_update",monster_on_update) RegisterScriptCallback("actor_on_item_use",actor_on_item_use) RegisterScriptCallback("save_state",save_state) RegisterScriptCallback("load_state",load_state) end